package com.zlyx.easy.access.abstracts;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import com.zlyx.easy.access.interceptors.ResultInterceptor;
import com.zlyx.easy.core.loggers.Logger;
import com.zlyx.easy.core.map.Maps;
import com.zlyx.easy.core.spring.SpringUtils;
import com.zlyx.easy.core.utils.MethodUtils;
import com.zlyx.easy.core.utils.ObjectUtils;
import com.zlyx.easy.database.enums.SqlType;
import com.zlyx.easy.database.interceptors.AbstractResultSetInterceptor;
import com.zlyx.easy.database.local.ReturnType;

/**
 * @Auth 赵光
 * @Describle
 * @2019年1月12日 下午12:47:32
 */
public class AbstractSqlExcutor extends AbstractResultSetInterceptor {

	protected Connection conn;

	public AbstractSqlExcutor(Connection conn) {
		this.conn = conn;
	}

	public Object excute(Class<?> tClass, AbstractSqlAssembler assembler) throws SQLException {
		PreparedStatement pst = null;
		try {
			if (assembler.getSql().contains(";") && SqlType.Select != assembler.getType()) {
				return batch(assembler);
			}
			pst = conn.prepareStatement(assembler.getSql());
			if (SqlType.Select == assembler.getType()) {
				return doInterceptor(tClass, assembler, pst.executeQuery());
			}
			pst.execute();
			return true;
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
//			conn.close();
		}
		return null;
	}

	public boolean batch(AbstractSqlAssembler assembler) throws SQLException {
		try {
			conn.setAutoCommit(false);
			Statement stat = conn.createStatement();
			for (String sql : assembler.getSql().split(";")) {
				stat.addBatch(sql);
			}
			stat.executeBatch();
			conn.setAutoCommit(true);
		} catch (Exception e) {
			Logger.err(e);
			conn.rollback();
			return false;
		}
		return true;
	}

	/**
	 * 结果集拦截器
	 * 
	 * @param tClass
	 * @param assembler
	 * @param res
	 * @return
	 * @throws Exception
	 */
	private Object doInterceptor(Class<?> tClass, AbstractSqlAssembler assembler, Object res) throws Exception {
		Collection<ResultInterceptor> interceptors = SpringUtils.getBeansOfType(ResultInterceptor.class).values();
		if (ObjectUtils.isNotEmpty(interceptors) && interceptors.size() != 0) {
			Object result = null;
			for (ResultInterceptor interceptor : interceptors) {
				result = interceptor.doInterceptor(assembler, res);
				if (result != null) {
					res = result;
				}
			}
		}
		if (SqlType.Select == assembler.getType() && res instanceof ResultSet) {
			res = getDefaultResult((ResultSet) res, tClass, assembler.getMethod());
		}
		return res;
	}

	/**
	 * 默认返回List<Map<String, Object>>对象
	 * 
	 * @param res
	 * @param method
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public Object getDefaultResult(ResultSet res, Class<?> tClass, Method method) throws Exception {
		try {
			Type type = MethodUtils.getReturnType(method);
			List list = new ArrayList<>();
			List<String> columns = new ArrayList<String>();
			ResultSetMetaData rsmd = res.getMetaData();
			for (int i = 0, n = rsmd.getColumnCount(); i < n; i++) {
				columns.add(rsmd.getColumnLabel(i + 1));
			}
			if (type.getClass().getName().contains("sun.reflect.generics")) {
				if (tClass == null) {
					tClass = ReturnType.getType();
				}
				list = getResults(tClass, res);
			} else if (!((Class<?>) type).getName().contains("java")) {
				list = getResults((Class<?>) type, res);
			} else {
				while (res.next()) {
					Map<String, Object> row = Maps.newMap();
					for (int i = 0, n = columns.size(); i < n; i++) {
						String name = columns.get(i);
						row.put(name.toLowerCase(Locale.ENGLISH), res.getObject(i + 1));
					}
					list.add(row);
				}
			}
			return list;
		} finally {
			if (res != null) {
				try {
					res.close();
				} catch (Exception e) {
				}
			}
		}
	}
}
